// SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
/* Copyright (C) 2016-2018, LomboTech Co.Ltd.
 * Author: lomboswer <lomboswer@lombotech.com>
 *
 * i2ccqi.c - i2ccqi driver code for LomboTech
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 */
#include "./i2c.h"
#include <linux/slab.h>

/* i2c register configuration,excluding fifo */
#define		REG_CFG_NUM		5

/* The maximum number of cmds that each list can contain,
 * excluding the header and tail,MAX 253 cmds
 */
#define		LIST_MAX_CMD			253

#define		HEAD_AND_TAIL			2
#define		CMD_DELAY_100US			0x960
#define		CMD_MAX_DELAY_UNIT_100US	27
#define		REG_DELAY			0XFFFFFFFF
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
/* #define DE_BUG */

#ifdef DE_BUG
#define PRT_DE_BUG	PRT_INFO
#else
#define PRT_DE_BUG(x...)	do {} while (0)
#endif

enum data_type {
	REG,
	VAL
};

enum data_byte_num {
	BYTE0_7,
	BYTE8_15
};

struct reg_val {
	u32	reg;
	u32	val;
};

typedef union {
	u64 val;
	struct {
		u32 command		: 8;/*'c'*/
		u32 queue		: 8;/*'q'*/
		u32 interpreter		: 8;/*'i'*/
		u32 resv0		: 7;
		u32 loop		: 1;/*the loop mode*/

		/*the length of the current command list*/
		u32 current_length	: 8;
		u32 resv1		: 8;
		u32 wt_threshold	: 16;/*the wait time out threshold*/
	} bits;
} cqi_head;

typedef union {
	u64 val;
	struct {
		u32 reg_offset	: 20;/*register offset*/
		u32 length	: 8;/*the number of data read/write*/
		u32 m		: 2;/*operation mode write-0/read-1/waite-2*/
		u32 c		: 1;/*consecutive mode,& wait value*/
		u32 w		: 1;/*wait mode*/

		u32 data	: 32;/*the data reg/write*/
	} bits;
} cqi_cmd;

typedef union {
	u64 val;
	struct {
		/* the low memory address of the next command list*/
		u32 address	: 32;

		u32 next_length	: 8;/*the length of the next command list*/
		u32 resv0	: 8;
		u32 hadderss	: 2;/*the hign memory address*/
		u32 resv1	: 14;
	} bits;
} cqi_tail;

static inline void lombo_cqi_stop(struct lombo_i2c *i2c, int ret);

static inline int lombo_set_queue_init_cmd(struct lombo_i2c_cqi *cqi,
						u32 reg, u32 d);

/* configuration during I2C transmission */
static void lombo_i2c_transmit_config(struct lombo_i2c *i2c)
{
	struct lombo_i2c_cqi *cqi = &i2c->cqi;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	u32 tx_len, dev_addr;

	tx_len	 = (qpara->reg_bit + qpara->data_bit) / 8;
	dev_addr = qpara->dev_addr;
	csp_i2c_transmit_cfg(tx_len, dev_addr);
}

/* configure the header for each list in the queue*/
static int lombo_set_queue_head(struct lombo_i2c_cqi *cqi)
{
	int i;
	cqi_head *head = NULL;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	u32 cmd_cnt = qpara->cmd_list_cnt;

	if (cmd_cnt != 0) {
		PRT_ERR("cnt: %d", cmd_cnt);
		return -EINVAL;
	}

	for (i = 0; i < qpara->list_num; i++) {
		head = (cqi_head *)&cqi->list_queue[i][qpara->cmd_list_cnt];
		head->bits.command = 0x63;
		head->bits.queue = 0x71;
		head->bits.interpreter = 0x69;
		head->bits.current_length = qpara->list_cmd_num - 1;
	}

	if (qpara->tail_list_config_num != 0)
		head->bits.current_length = qpara->tail_list_cmd_num - 1;

	qpara->cmd_list_cnt++;
	return 0;
}

/* configure register commands for each configuration in all lists */
static inline int lombo_set_queue_init_cmd(struct lombo_i2c_cqi *cqi,
						u32 reg, u32 d)
{
	int i, j;
	cqi_cmd *cmd;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	u32 cmd_cnt = qpara->cmd_list_cnt;

	if (cmd_cnt > qpara->config_cmd_num) {
		PRT_ERR("cnt: %d, cmd: %d\n", cmd_cnt, (REG_CFG_NUM +
							qpara->data_byte));
		return -EINVAL;
	}

	for (i = 0; i < qpara->list_num; i++) {
		for (j = 0; j < qpara->list_config_num; j++) {
			cmd = (cqi_cmd *)&cqi->list_queue[i][cmd_cnt +
				(j * qpara->config_cmd_num)];
			if (cmd->bits.m == 3)
				continue;

			cmd->bits.reg_offset = reg;
			cmd->bits.data = d;
		}
	}

	qpara->cmd_list_cnt++;

	return 0;
}

/* configure single cmd as delay */
static inline void lombo_set_delay_cmd(cqi_cmd *cmd, u32 us_100)
{
	u32 delay = CMD_DELAY_100US * us_100;

	cmd->bits.w = 0;
	cmd->bits.c = 0;
	cmd->bits.m = 3;
	cmd->bits.length = 0;
	cmd->bits.reg_offset = 0;
	cmd->bits.data = delay;

}
/* set the configuration of the cmd to delay */
static inline void lombo_set_queue_delay_cmds(struct lombo_i2c_cqi *cqi,
						cqi_cmd *cmd, u32 delay)
{
	u32 i;
	u32 delay_100us = delay * 10;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;

	for (i = 0; i < qpara->config_cmd_num; i++) {
		if (delay_100us >= CMD_MAX_DELAY_UNIT_100US) {
			lombo_set_delay_cmd(&cmd[i], CMD_MAX_DELAY_UNIT_100US);
			delay_100us -= CMD_MAX_DELAY_UNIT_100US;
		} else {
			lombo_set_delay_cmd(&cmd[i], delay_100us);
			delay_100us = 0;
		}
	}

	if (delay_100us)
		PRT_ERR("The delay is too long, %d ms!\n", delay);
}

/* set the corresponding cmd iin each list as delay
 * max 2.7ms :(0xffff * 1000/24000000),24M clock,(24c16a test)
 */
static inline int lombo_set_queue_delay_cmd(struct lombo_i2c_cqi *cqi)
{
	int i, j;
	cqi_cmd *cmd;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	u32 cmd_cnt = qpara->cmd_list_cnt;


	if (cmd_cnt > qpara->config_cmd_num) {
		PRT_ERR("cnt: %d, num:%d", cmd_cnt, qpara->config_cmd_num);
		return -EINVAL;
	}

	for (i = 0; i < qpara->list_num; i++) {
		for (j = 0; j < qpara->list_config_num; j++) {
			cmd = (cqi_cmd *)&cqi->list_queue[i][cmd_cnt +
				(j * qpara->config_cmd_num)];
			cmd->bits.w = 0;
			cmd->bits.c = 0;
			cmd->bits.m = 3;
			cmd->bits.length = 0;
			cmd->bits.reg_offset = 0;
			cmd->bits.data = 0xffff;/* 33us */
		}
	}

	qpara->cmd_list_cnt++;

	return 0;
}

/* set the corresponding cmd in each list as fifo operation,
 * configure register or data
 */
static inline int lombo_set_queue_data_cmd(struct lombo_i2c_cqi *cqi,
						u32 dt, u32 dt_bit)
{
	int i, j;
	u32 data = 0;
	cqi_cmd *cmd;
	u32 data_num = 0;
	u32 reg, val;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	struct reg_val *msgs = (struct reg_val *)qpara->msgs;
	u32 cmd_cnt = qpara->cmd_list_cnt;

	get_i2coffsetof(REG_DATA_CMD, &reg, &val);

	if (cmd_cnt > qpara->config_cmd_num) {
		PRT_ERR("cnt: %d, num:%d", cmd_cnt, qpara->config_cmd_num);
		return -EINVAL;
	}

	for (i = 0; i < qpara->list_num; i++) {
		for (j = 0; j < qpara->list_config_num; j++) {

			cmd = (cqi_cmd *)&cqi->list_queue[i][cmd_cnt +
				(j * qpara->config_cmd_num)];

			cmd->bits.reg_offset = reg;

			if (dt == REG)
				data = msgs[data_num].reg;
			else
				data = msgs[data_num].val;

			if (dt_bit == BYTE0_7) {
				cmd->bits.data = (u8)(data & 0xff);

				if ((msgs[data_num].reg == REG_DELAY) &&
					(dt == VAL)) {
					cmd = (cqi_cmd *)&cqi->list_queue[i][1 +
					(j * qpara->config_cmd_num)];
					lombo_set_queue_delay_cmds(cqi,
						cmd, msgs[data_num].val);
				}
			} else
				cmd->bits.data = (u8)(data >> 8);
			data_num++;

			if (data_num >= qpara->msg_num)
				break;
		}
	}

	qpara->cmd_list_cnt++;

	return 0;
}

/* set the corresponding cmd in each list to wait */
static inline int lombo_set_queue_wait_cmd(struct lombo_i2c_cqi *cqi,
						u32 reg, u32 d)
{
	int i, j;
	cqi_cmd *cmd;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	u32 cmd_cnt = qpara->cmd_list_cnt;

	if (cmd_cnt > qpara->config_cmd_num) {
		PRT_ERR("cnt: %d, num:%d", cmd_cnt, qpara->config_cmd_num);
		return -EINVAL;
	}

	for (i = 0; i < qpara->list_num; i++) {
		for (j = 0; j < qpara->list_config_num; j++) {
			cmd = (cqi_cmd *)&cqi->list_queue[i][cmd_cnt +
				(j * qpara->config_cmd_num)];
			if (cmd->bits.m == 3)
				continue;

			cmd->bits.w = 1;
			cmd->bits.c = 1;
			cmd->bits.m = 2;
			/* cmd->bits.length = 1; */
			cmd->bits.reg_offset = reg;
			cmd->bits.data = d;
		}
	}

	qpara->cmd_list_cnt++;

	return 0;
}

/* set the tail of each list in the queue */
static int lombo_set_queue_tail(struct lombo_i2c_cqi *cqi)
{
	int i;
	cqi_tail *tail = NULL;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;

	if ((qpara->list_num == 1) && (qpara->tail_list_config_num == 0)) {
		tail = (cqi_tail *)&cqi->list_queue[0][qpara->list_cmd_num - 1];
		tail->val = 0;

		goto out;
	}

	if (qpara->tail_list_config_num == 0) {
		for (i = 0; i < qpara->list_num; i++) {
			tail = (cqi_tail *)&cqi->list_queue[i]
					[qpara->list_cmd_num - 1];
			tail->bits.next_length = qpara->list_cmd_num - 1;

			if (qpara->list_num == 1)
				tail->bits.address = (u32)(cqi->dma_add +
					((i + 1) * 8 * (qpara->list_cmd_num)));
			else if (qpara->list_num > 1)
				tail->bits.address = (u32)(cqi->dma_add +
					(i + 1) * 2048);
		}
		tail->val = 0;

	} else {
		for (i = 0; i < qpara->list_num - 2; i++) {
			tail = (cqi_tail *)&cqi->list_queue[i]
				[qpara->list_cmd_num - 1];
			tail->bits.next_length = qpara->list_cmd_num - 1;

			if (qpara->list_num == 1)
				tail->bits.address = (u32)(cqi->dma_add +
					((i + 1) * 8 * (qpara->list_cmd_num)));
			else
				tail->bits.address = (u32)(cqi->dma_add +
					(i + 1) * 2048);

			PRT_DE_BUG("aligen addr: 0x%08x\n", tail->bits.address);
		}

		tail = (cqi_tail *)&cqi->list_queue[qpara->list_num - 2]
					[qpara->list_cmd_num - 1];
		tail->bits.next_length = qpara->tail_list_cmd_num - 1;

		if (qpara->list_num == 1)
			tail->bits.address = (u32)(cqi->dma_add +
				((i + 1) * 8 * (qpara->list_cmd_num)));
		else
			tail->bits.address = (u32)(cqi->dma_add +
				(i + 1) * 2048);

		PRT_DE_BUG("addr : 0x%08x\n", tail->bits.address);

		tail = (cqi_tail *)&cqi->list_queue[qpara->list_num - 1]
					[qpara->tail_list_cmd_num - 1];
		tail->val = 0;

	}

	if (qpara->tail_list_config_num != 0) {
		tail = (cqi_tail *)&cqi->list_queue[qpara->list_num - 1][0];

		for (i = qpara->tail_list_cmd_num; i < qpara->list_cmd_num; i++)
			tail[i].val = 0;
	}

out:
	qpara->cmd_list_cnt++;

	if (qpara->cmd_list_cnt == (REG_CFG_NUM +
		qpara->data_byte + HEAD_AND_TAIL))
		return 0;

	PRT_ERR("qpara->cmd_list_cnt: %d\n", qpara->cmd_list_cnt);
	return -EINVAL;
}

/* configure i2c initialization command */
static void lombo_set_queue_init_cmds(struct lombo_i2c_cqi *cqi)
{
	u32 reg, val;

	/* enable auto mode */
	get_i2coffsetof(REG_CTL, &reg, &val);
	lombo_set_queue_init_cmd(cqi, reg, val);

	/* lombo_set_queue_delay_cmd(cqi); */

	/* lombo_set_queue_delay_cmd(cqi); */

	/* set auto dir r/w */
	/* enable_i2c */
	get_i2coffsetof(REG_ENABLE, &reg, &val);
	lombo_set_queue_init_cmd(cqi, reg, val);
}

/* configure i2c data or reg command */
static void lombo_set_queue_data_cmds(struct lombo_i2c_cqi *cqi)
{
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	u32	reg_bit = qpara->reg_bit;
	u32	data_bit = qpara->data_bit;

	if (reg_bit == 16)
		lombo_set_queue_data_cmd(cqi, REG, BYTE8_15);

	lombo_set_queue_data_cmd(cqi, REG, BYTE0_7);

	if (data_bit == 16)
		lombo_set_queue_data_cmd(cqi, VAL, BYTE8_15);

	lombo_set_queue_data_cmd(cqi, VAL, BYTE0_7);
}

/* configure i2c exit command */
static void lombo_set_queue_exit_cmds(struct lombo_i2c_cqi *cqi)
{
	u32 reg, val;

	get_i2coffsetof(REG_INT_PD, &reg, &val);
	lombo_set_queue_wait_cmd(cqi, reg, I2C_INT_AUTO_FINISH);

	/* clear int pd */
	get_i2coffsetof(REG_INT_CLR, &reg, &val);
	lombo_set_queue_init_cmd(cqi, reg, val);

	/* dis auto i2c */
	get_i2coffsetof(REG_ENABLE, &reg, &val);
	val = set_dis_auto_i2c();
	lombo_set_queue_init_cmd(cqi, reg, val);
}

/* config cqi queue */
static int lombo_cqi_config(struct lombo_i2c_cqi *cqi)
{
	int ret;

	ret = lombo_set_queue_head(cqi);
	if (ret)
		return ret;

	lombo_set_queue_init_cmds(cqi);
	lombo_set_queue_data_cmds(cqi);
	lombo_set_queue_exit_cmds(cqi);

	ret = lombo_set_queue_tail(cqi);
	if (ret)
		return ret;

	return 0;
}
/* regular parse msgs and save configuration */
static void lombo_cqi_set_queue_regular(struct lombo_i2c_cqi *cqi, u32 addr,
					u32 reg_bit, u32 data_bit)
{
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;

	qpara->dev_addr = addr;
	qpara->reg_bit = reg_bit;
	qpara->data_bit = data_bit;
	qpara->data_byte = (qpara->reg_bit + qpara->data_bit) / 8;
	qpara->config_cmd_num = REG_CFG_NUM + qpara->data_byte;
	qpara->list_config_num = LIST_MAX_CMD / qpara->config_cmd_num;

	PRT_DE_BUG("qpara->config_cmd_num:%d\n", qpara->config_cmd_num);
	PRT_DE_BUG("qpara->data_byte:%d\n", qpara->data_byte);
	PRT_DE_BUG("qpara->list_config_num:%d\n", qpara->list_config_num);
}

/* dynamic parse msgs and save configuration */
static void lombo_cqi_set_queue_dynamic(struct lombo_i2c_cqi *cqi, void *msgs,
				    u32 *tran_count, u32 msg_num_total)
{
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	struct reg_val *rmsgs = (struct reg_val *)msgs;

	/* in current buffer, maximum msg that can be tranmitted */
	u32 max_config_num = qpara->list_config_num * CONFIG_CQI_QUEUE_BUF_SIZE;
	u32 msg_num_rest;
	u32 tran_count_tmp;
	u32 tran_count_save;

	/* all msgs can be tranmitted in one time */
	if (max_config_num >= msg_num_total) {
		*tran_count = 1;
		qpara->msgs = rmsgs;
		qpara->msg_num = msg_num_total;
	} else {
		tran_count_tmp = *tran_count;
		tran_count_save = msg_num_total / max_config_num;
		tran_count_save += (tran_count_save * max_config_num
					> msg_num_total) ? 0 : 1;
		/* tran_count first assign a valid value here */
		*tran_count = (tran_count_tmp != 0) ? tran_count_tmp :
				tran_count_save;

		qpara->msgs = &rmsgs[(tran_count_save - *tran_count) *
				max_config_num];
		msg_num_rest = msg_num_total - (tran_count_save - *tran_count) *
				max_config_num;

		PRT_DE_BUG("msg_num_rest: %d,tran_count_save: %d\n",
			   msg_num_rest, tran_count_save);

		/* require multiple split to trans */
		if (msg_num_rest > max_config_num)
			qpara->msg_num = max_config_num;
		else	/* last msgs num to be trans */
			qpara->msg_num = msg_num_rest;
	}

	if (qpara->list_config_num > qpara->msg_num)
		qpara->list_config_num = qpara->msg_num;

	qpara->list_num = qpara->msg_num / qpara->list_config_num;
	qpara->tail_list_config_num = qpara->msg_num % qpara->list_config_num;
	qpara->list_cmd_num = qpara->list_config_num * qpara->config_cmd_num
					+ HEAD_AND_TAIL;

	if (qpara->tail_list_config_num != 0) {
		qpara->list_num += 1;

		qpara->tail_list_cmd_num = qpara->tail_list_config_num *
					qpara->config_cmd_num + HEAD_AND_TAIL;
	}

	PRT_DE_BUG("*tran_count: %d\n", *tran_count);
	PRT_DE_BUG("qpara->msg_num: %d\n", qpara->msg_num);
	PRT_DE_BUG("qpara->list_num:%d\n", qpara->list_num);
	PRT_DE_BUG("qpara->tail_list_config_num:%d\n",
		   qpara->tail_list_config_num);
	PRT_DE_BUG("qpara->list_cmd_num:%d\n", qpara->list_cmd_num);
	PRT_DE_BUG("qpara->tail_list_cmd_num:%d\n", qpara->tail_list_cmd_num);
}

/* think about cache,apply for dma buf */
void lombo_cqi_req_queue_buf(struct lombo_i2c *i2c)
{
	int i;
	struct lombo_i2c_cqi *cqi = &i2c->cqi;

	cqi->list_queue = (u64 **)kmalloc_array(CONFIG_CQI_QUEUE_BUF_SIZE,
				sizeof(u64 *), GFP_KERNEL);

	cqi->buf = dma_zalloc_coherent(i2c->dev,
			CONFIG_CQI_QUEUE_BUF_SIZE * 2048,
			&cqi->dma_add, GFP_KERNEL);
	if (!cqi->buf)
		PRT_ERR("dma_zalloc_coherent FAIL!\n");

	for (i = 0; i < CONFIG_CQI_QUEUE_BUF_SIZE; i++)
		cqi->list_queue[i] = &cqi->buf[i * 256];
}

void lombo_cqi_unreq_queue_buf(struct lombo_i2c *i2c)
{
	struct lombo_i2c_cqi *cqi = &i2c->cqi;

	dma_free_coherent(i2c->dev, CONFIG_CQI_QUEUE_BUF_SIZE * 2048,
			cqi->buf, cqi->dma_add);

	kfree(cqi->list_queue);
}
/**
 * lombo_cqi_start_msgs - start transfer the messages
 * @i2c: point to i2c host structure
 * @msgs: start address of the messages to transfer
 * @num: number of the messages to transfer
 *
 * return 0 means ok; others means error
 */
static int lombo_cqi_start_msgs(struct lombo_i2c *i2c)
{
	int ret = 0;
	struct lombo_i2c_cqi *cqi = &i2c->cqi;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	unsigned long timeout;

	if ((cqi == NULL) || (qpara->msgs == NULL)) {
		PRT_ERR("i2c=0x%p,msgs=0x%p\n", cqi, qpara->msgs);
		return -EINVAL;
	}

	csp_i2ccqi_set_addr(i2c->base, (u32)(cqi->dma_add));
	PRT_DE_BUG("addr : 0x%08x\n", (u32)(cqi->dma_add));

	if (qpara->list_num == 1 && qpara->tail_list_cmd_num != 0)
		csp_i2ccqi_set_addr_len(i2c->base,
					qpara->tail_list_cmd_num - 1);
	else
		csp_i2ccqi_set_addr_len(i2c->base, qpara->list_cmd_num - 1);

	csp_i2c_set_rt_len(i2c->base, 0, qpara->data_byte);
	csp_i2c_set_dev_addr(i2c->base, qpara->dev_addr, 0);

	/* clear all int */
	lombo_i2c_transmit_config(i2c);

	ret = lombo_cqi_config(cqi);
	if (ret)
		return ret;

#ifdef DE_BUG
	{
		int i, j;

		for (i = 0; i < qpara->list_num; i++) {
			PRT_DE_BUG("msg_num_x:%d\n", i);
			for (j = 0; j < qpara->list_cmd_num; j++) {

				u32 *a = (u32 *)&cqi->list_queue[i][j];

				PRT_DE_BUG("0x%08x  0x%08x", a[0], a[1]);
			}
			PRT_DE_BUG("\n");
		}
	}
#endif
	csp_i2ccqi_clr_pd(i2c->base, I2C_INT_ALL_MASK);

	csp_i2ccqi_enable_ints(i2c->base, I2C_CQI_INT_AF | I2C_CQI_INT_ERR);
	csp_i2ccqi_start_enable(i2c->base, 1);

	timeout = wait_event_timeout(i2c->wait, qpara->msgs == NULL, HZ * 5);
	if (timeout == 0) {
		PRT_DBG("wait event timeout!\n");
		lombo_cqi_stop(i2c, -EIO);
	} else {
		PRT_DBG("wait event successful\n");
	}

	PRT_DBG("%p: incomplete xfer (%d)\n", i2c->base, ret);
	PRT_TRACE_END("\n");
	return ret;
}

/**
 * lombo_cqi_master_complete
 * @i2c: point to i2c host structure
 * @ret: return code, 0 means ok; others means error
 *
 * complete the message and wake up the caller, using the given return code,
 * or zero to mean ok.
 */
static inline void lombo_cqi_master_complete(struct lombo_i2c *i2c,
								int ret)
{
	struct lombo_i2c_cqi *cqi = &i2c->cqi;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;

	PRT_TRACE_BEGIN("i2c=0x%p,ret=%d\n", i2c, ret);
	PRT_DBG("%p: master_complete %d\n", i2c->base, ret);

	qpara->msgs = NULL;
	qpara->msg_num = 0;

	wake_up(&i2c->wait);
	PRT_TRACE_END("\n");
}

/**
 * lombo_cqi_stop - stop i2c transfer, using the given return code
 * @i2c: point to i2c host structure
 * @ret: return code, 0 means ok; others means error
 */
static inline void lombo_cqi_stop(struct lombo_i2c *i2c, int ret)
{
	PRT_TRACE_BEGIN("i2c=0x%p,ret=%d\n", i2c, ret);
	PRT_DBG("%p: STOP\n", i2c->base);

	csp_i2ccqi_disable_ints(i2c->base, I2C_INT_ALL_MASK);
	csp_i2ccqi_clr_pd(i2c->base, I2C_INT_ALL_MASK);
	csp_i2ccqi_start_enable(i2c->base, 0);
	csp_i2c_auto_enable(i2c->base, 0);

	lombo_cqi_master_complete(i2c, ret);
	PRT_TRACE_END("");
}

/**
 * lombo_cqi_irq
 *
 * top level IRQ servicing routine
 */
irqreturn_t lombo_cqi_irq(struct lombo_i2c *i2c, int irqno, void *dev_id)
{
	u32 int_status = 0;
	irqreturn_t result = IRQ_HANDLED;

	PRT_TRACE_BEGIN("irqno=%d,dev_id=0x%p", irqno, dev_id);

	int_status = csp_i2ccqi_get_int_pd(i2c->base);
	PRT_DE_BUG("IRQ:0X%08x\n", int_status);
	if (!int_status) {
		PRT_WARN("%p: no i2c interrupt pendings\n", i2c->base);
		result = IRQ_NONE;
		goto out;
	}

	/* finish the transfer */
	if ((int_status & I2C_CQI_INT_ERR) != 0) {
		PRT_ERR("%p: CQI ERR 0X%08x\n", i2c->base, int_status);
		csp_i2ccqi_set_reset(i2c->base, 0);
		csp_i2ccqi_set_reset(i2c->base, 1);
		lombo_cqi_stop(i2c, -EIO);
		goto out;
	}

	if ((int_status & (I2C_CQI_INT_AF)) != 0) {
		PRT_DE_BUG("%p: i2ccqi all finish\n", i2c->base);
		lombo_cqi_stop(i2c, 0);
		goto out;
	}

out:

	PRT_TRACE_END("\n");
	return result;
}

/**
 * lombo_cqi_xfer
 */
int lombo_cqi_xfer(struct i2c_adapter *adap, u32 addr,
			void *msgs, u32 sum,
			u32 reg_bit, u32 data_bit)
{
	struct lombo_i2c *i2c = (struct lombo_i2c *)adap->algo_data;
	struct lombo_i2c_cqi *cqi = &i2c->cqi;
	struct lombo_i2c_cqi_qpara *qpara = &cqi->qpara;
	u32 tran_count = 0;
	int retry;
	int ret;

	pm_runtime_get_sync(&adap->dev);

	csp_i2ccqi_enable_cs(1);

	if (csp_i2ccqi_chk_status(i2c->base, I2C_CQI_STATUS_BUSY) != 0)
		PRT_ERR("%p: timeout waiting for bus idle\n", i2c->base);

	csp_i2ccqi_set_reset(i2c->base, 0);
	csp_i2ccqi_set_reset(i2c->base, 1);

	ret = csp_i2ccqi_chk_status(i2c->base, I2C_CQI_STATUS_RS_DONE);
	if (!ret) {
		PRT_ERR("rst done err\n");
		return ret;
	}

	do {
		memset(qpara, 0, sizeof(struct lombo_i2c_cqi_qpara));

		lombo_cqi_set_queue_regular(cqi, addr, reg_bit, data_bit);
		lombo_cqi_set_queue_dynamic(cqi, msgs, &tran_count, sum);

		for (retry = 0; retry < adap->retries; retry++) {

			ret = lombo_cqi_start_msgs(i2c);
			if (ret == 0) {
				memset(cqi->buf, 0, qpara->list_num * 2048);

				tran_count--;
				if (tran_count)
					PRT_DE_BUG("next trans\n");
				else
					PRT_DE_BUG("trans finish\n");
				break;
			}

			PRT_ERR("lombo_cqi_start_msgs: %d\n", ret);
			PRT_DBG("%p: Retrying transmission (%d)\n",
					i2c->base, retry);
			udelay(100);
		}

	} while (tran_count > 0 && retry < adap->retries);

	csp_i2ccqi_enable_cs(0);

	pm_runtime_put_sync(&adap->dev);

	PRT_TRACE_END("\n");

	if (tran_count == 0)
		return ret;

	return -EREMOTEIO;
}

static ssize_t cqi_reparse_show(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	return 0;
}

struct reg_val test_arr0[] = {
	{0x00, 0x0101},
	{0x10, 0x0101},
	{0x20, 0x0101},

	{0x30, 0x0101},
	{0x40, 0x0101},
	{0x50, 0x0101},
	{0x60, 0x0101},
	{0x70, 0x0101},
	{0x80, 0x0101},
	{0x90, 0x0101},

	{0x0a, 0x01},
	{0x0b, 0x01},
	{0x0c, 0x01},
	{0x0d, 0x01},
	{0x0e, 0x01},
	{0x0f, 0x01},
	{0x10, 0x01},
	{0x11, 0x01},
	{0x12, 0x01},
	{0x13, 0x01},

	{0x20, 0x01},
	{0x21, 0x01},
	{0x22, 0x01},
	{0x23, 0x01},
	{0x24, 0x01},
	{0x25, 0x01},
	{0x26, 0x01},
	{0x27, 0x01},
	{0x28, 0x01},
	{0x29, 0x01},

	{0x30, 0x01},
	{0x31, 0x01},
	{0x32, 0x01},
	{0x33, 0x01},
	{0x34, 0x01},
	{0x35, 0x01},
	{0x36, 0x01},
	{0x37, 0x01},
	{0x38, 0x01},
	{0x39, 0x01},
};

struct reg_val test_arr1[] = {
	{0x00, 0xffff},
	{0x10, 0xffff},
	{0x20, 0xffff},

	{0x30, 0xffff},
	{0x40, 0xffff},
	{0x50, 0xffff},
	{0x60, 0xffff},
	{0x70, 0xffff},
	{0x80, 0xffff},
	{0x90, 0xffff},

	{0x0a, 0xff},
	{0x0b, 0xff},
	{0x0c, 0xff},
	{0x0d, 0xff},
	{0x0e, 0xff},
	{0x0f, 0xff},
	{0x10, 0xff},
	{0x11, 0xff},
	{0x12, 0xff},
	{0x13, 0xff},

	{0x20, 0xff},
	{0x21, 0xff},
	{0x22, 0xff},
	{0x23, 0xff},
	{0x24, 0xff},
	{0x25, 0xff},
	{0x26, 0xff},
	{0x27, 0xff},
	{0x28, 0xff},
	{0x29, 0xff},

	{0x30, 0xff},
	{0x31, 0xff},
	{0x32, 0xff},
	{0x33, 0xff},
	{0x34, 0xff},
	{0x35, 0xff},
	{0x36, 0xff},
	{0x37, 0xff},
	{0x38, 0xff},
	{0x39, 0xff},
};

static ssize_t cqi_reparse_store(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t count)
{
	int ret;
	unsigned int state;
	struct i2c_adapter *adapter = NULL;
	struct i2c_client *client = NULL;
	struct i2c_board_info info;
	u32 a = 0, b = 0;

	if (kstrtouint(buf, 0, &state)) {
		PRT_ERR("invalid parameter\n");
		return count;
	}

	a = state / 100;
	b = (state % 100) % 41;

#if defined(CONFIG_ARCH_LOMBO_N7V5) || defined(CONFIG_ARCH_LOMBO_N5V1)
	adapter = i2c_get_adapter(3);
#else
	adapter = i2c_get_adapter(4);
#endif
	if (adapter == NULL) {
		PRT_ERR("Can't find i2c adapter %d\n", 4);
		ret = -ENODEV;
		goto out;
	}

	memset(&info, 0, sizeof(struct i2c_board_info));
	strncpy(info.type, "cqi", 5);
	info.addr = 0x50;

	client = i2c_new_device(adapter, &info);
	if (client == NULL) {
		PRT_ERR("Can't add dev to adapter\n");
		i2c_put_adapter(adapter);
		ret = -ENODEV;
		goto out;
	}

	if (a)
		ret = lombo_cqi_xfer(adapter, 0x50, test_arr0,
			b, 8, 16);
	else
		ret = lombo_cqi_xfer(adapter, 0x50, test_arr1,
			b, 8, 16);

	if (ret != 0)
		PRT_ERR("lombo_cqi_xfer: %d\n", ret);

	i2c_unregister_device(client);
	i2c_put_adapter(adapter);

out:
	return count;
}

static DEVICE_ATTR_RW(cqi_reparse);

/* lombo_i2c_cqi_node
 *
 * called by the bus driver when a suitable device is found
 */
void lombo_cqi_node(struct platform_device *pdev)
{
	int ret;

	ret = device_create_file(&pdev->dev, &dev_attr_cqi_reparse);
	if (ret)
		PRT_ERR("dev_attr_cqi_reparse failed (%d)\n", ret);
	PRT_INFO("i2c support cqi module\n");
}

